﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace Might.ServiceServer.AuthenticationService {
    
    public class AuthenticationService : IAuthenticationService {

        /// <summary>
        /// Path to a XML file with user database.
        /// </summary>
        //private string XML_FILE = Path.Combine(System.Environment.CurrentDirectory,"XML\\data.xml");
        private string XML_FILE = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XML\\data.xml");


        /// <summary>
        /// Verify logged user.
        /// </summary>
        /// <param name="Login">Authentication credentials.</param>
        /// <returns>Authentication class: 
        /// - Anonymous user, when authentication failed.
        /// - Trusted user with all servers and credentials, when authentication passed.
        /// </returns>
        public Authentication GetAuthenticationWithException(Authentication Login) {

            XmlSerializer mySerializer = new XmlSerializer(typeof(UserDatabase));
            FileStream myFileStream;

            try {
                myFileStream = new FileStream(XML_FILE, FileMode.Open);
            }
            catch {
                throw new FaultException<Exception>(new Exception("Authentication XML not awailable or access permited!"));
            }


            UserDatabase userDatabase = (UserDatabase)mySerializer.Deserialize(myFileStream);

            myFileStream.Close();

            foreach (Authentication user in userDatabase.Users) {
                if (Login.Username.Equals(user.Username)) {
                    if (Login.Password.Equals(user.Password)) {
                        Login.SvnList = user.SvnList;
                        return Login;
                    }
                    else {
                        //throw new Exception("Password incorect!");
                        throw new FaultException<Exception>(new Exception("Password incorect!"));
                    }
                }
            }

            //throw new Exception("Username or password don't match!");
            throw new FaultException<Exception>(new Exception("Username or password don't match!"));

           /* XmlDocument xml = new XmlDocument();
            xml.Load(XML_FILE);
            
            XmlElement root = xml.DocumentElement;
            String sXPathQuery = "//user[(might_username=\"" + Login.Username + "\") and (might_password=\"" + Login.Password + "\")]";
            XmlNodeList user = root.SelectNodes(sXPathQuery);

            if (user.Count == 0) {
                // Entry not found -> Authentication Error
                //sXPathQuery = "//user[might_username=\"Anonymous\"]";
                //user = root.SelectNodes(sXPathQuery);
                throw new Exception("Username or password don't match!");
            }
            else if (user.Count > 1) {
                throw new Exception("Multiple users with the same credentials!!!");
            }

            List<SvnRecord> svnlist = new List<SvnRecord>();
            
            // Parsing XML entry
            foreach (XmlNode userNode in user) {    // foreach tag USER
                if (userNode.HasChildNodes) {
                    XmlNodeList userInfo = userNode.ChildNodes;
                    foreach (XmlNode infoNode in userInfo) { // TAGs inside TAG USER (might username and password a servers)   
                        switch (infoNode.Name) {
                            case "might_username":
                                Login.Username = infoNode.InnerText;
                                break;
                            case "might_password":
                                Login.Password = infoNode.InnerText;
                                break;
                            case "servers":
                                XmlNodeList servers = infoNode.ChildNodes;
                                foreach (XmlNode serverInfoNode in servers) {   // foreach tag SERVER
                                    XmlNodeList serverInfo = serverInfoNode.ChildNodes;
                                    //ISvnRecord server = new SvnRecord();
                                    SvnRecord server = new SvnRecord();
                                    foreach (XmlNode serverNode in serverInfo) {    // TAGs inside TAG SERVER (avn address, svn username and svn password)
                                        switch (serverNode.Name) {
                                            case "address":
                                                server.SvnAddress = serverNode.InnerText;
                                                break;
                                            case "username":
                                                server.SvnUsername = serverNode.InnerText;
                                                break;
                                            case "password":
                                                server.SvnPassword = serverNode.InnerText;
                                                break;
                                        }
                                    }
                                    svnlist.Add(server);
                                }
                                break;
                        }
                    }
                }
            }
            Login.SvnList = svnlist;    // add list of svn servers to output message
            return Login;*/
        }

        public Authentication GetAuthentication(Authentication Login, out MyFaultContract myFault) {

            Authentication result = null;
            myFault = null;

            try {
                result = GetAuthenticationWithException(Login);
            }
            catch (FaultException<Exception> ex) {
                myFault = new MyFaultContract() {
                    FaultType = ex.Detail.GetType().FullName,
                    Message = ex.Detail.Message
                };
            }

            return result;

        }


        /// <summary>
        /// Create a new account in server database of Might users.
        /// </summary>
        /// <param name="Login">Authentication credentials with all the information (servers and their credentials).</param>
        /// <returns>True, if new account has created, False otherwise.</returns>
        public bool NewAuthentication(Authentication Login) {

            XmlSerializer mySerializer = new XmlSerializer(typeof(UserDatabase));
            FileStream myFileStream = new FileStream(XML_FILE, FileMode.Open);

            UserDatabase userDatabase = (UserDatabase)mySerializer.Deserialize(myFileStream);

            myFileStream.Close();

            foreach (Authentication user in userDatabase.Users) {
                if (Login.Username.Equals(user.Username)) {
                    throw new Exception("Userneme exists!");
                }
            }

            try {
                userDatabase.Users.Add(Login);
                StreamWriter myWriter = new StreamWriter(XML_FILE);
                mySerializer.Serialize(myWriter, userDatabase);
                myWriter.Close();
                return true;
            }
            catch {
                return false;
            }

/*            XmlDocument xml = new XmlDocument();
            xml.Load(XML_FILE);

            XmlElement root = xml.DocumentElement;
            String sXPathQuery = "//user[(might_username=\"" + Login.Username + "\") and (might_password=\"" + Login.Password + "\")]";
            XmlNodeList user = root.SelectNodes(sXPathQuery);

            if (user.Count == 0) {  // if there no same account, create one
                XmlElement newUser = xml.CreateElement("user");
                XmlElement newMightUsername = xml.CreateElement("might_username");
                newMightUsername.InnerText = Login.Username;
                XmlElement newMightPassword = xml.CreateElement("might_password");
                newMightPassword.InnerText = Login.Password;
                XmlElement newServers = xml.CreateElement("servers");

                foreach (SvnRecord server in Login.SvnList) {
                    XmlElement newServer = xml.CreateElement("server");
                    XmlElement newServerAddress = xml.CreateElement("address");
                    newServerAddress.InnerText = server.SvnAddress;
                    XmlElement newServerUsername = xml.CreateElement("username");
                    newServerUsername.InnerText = server.SvnUsername;
                    XmlElement newServerPassword = xml.CreateElement("password");
                    newServerPassword.InnerText = server.SvnPassword;
                    newServer.AppendChild(newServerAddress);
                    newServer.AppendChild(newServerUsername);
                    newServer.AppendChild(newServerPassword);
                    newServers.AppendChild(newServer);
                }

                newUser.AppendChild(newMightUsername);
                newUser.AppendChild(newMightPassword);
                newUser.AppendChild(newServers);

                root.AppendChild(newUser);
                xml.Save(XML_FILE);
                return true;
            }
            else {
                return false;
            }*/
        }

        /// <summary>
        /// Delete existing account in server database of Might users.
        /// </summary>
        /// <param name="Login"></param>
        /// <returns>True, if the account has deleted, False otherwise.</returns>
        public bool DeleteAuthentication(Authentication Login) {

            XmlSerializer mySerializer = new XmlSerializer(typeof(UserDatabase));
            FileStream myFileStream = new FileStream(XML_FILE, FileMode.Open);

            UserDatabase userDatabase = (UserDatabase)mySerializer.Deserialize(myFileStream);
            myFileStream.Close();
            Authentication userToDelete = null;

            foreach (Authentication user in userDatabase.Users) {
                if (Login.Username.Equals(user.Username) && Login.Password.Equals(user.Password)) {
                    userToDelete = user;
                    break;
                }
            }

            if (userToDelete != null) {
                try {
                    userDatabase.Users.Remove(userToDelete);
                    StreamWriter myWriter = new StreamWriter(XML_FILE);
                    mySerializer.Serialize(myWriter, userDatabase);
                    myWriter.Close();
                    return true;
                }
                catch {
                    return false;
                }
            }
            else {
                return false;
            }

/*            XmlDocument xml = new XmlDocument();
            xml.Load(XML_FILE);

            XmlElement root = xml.DocumentElement;
            String sXPathQuery = "//user[(might_username=\"" + Login.Username + "\") and (might_password=\"" + Login.Password + "\")]";
            XmlNodeList user = root.SelectNodes(sXPathQuery);

            if (user.Count == 1) {  // if there ONLY one account, delete it
                XmlNode deletedUser = root.SelectSingleNode(sXPathQuery);
                root.RemoveChild(deletedUser);
                xml.Save(XML_FILE);
                return true;
            }
            else {
                return false;
            }*/
        }

        public bool UpdateAuthentication(Authentication LastAuth, Authentication UpdateAuth) {
            
            XmlSerializer mySerializer = new XmlSerializer(typeof(UserDatabase));
            FileStream myFileStream = new FileStream(XML_FILE, FileMode.Open);

            UserDatabase userDatabase = (UserDatabase)mySerializer.Deserialize(myFileStream);
            myFileStream.Close();
            Authentication userToUpdate = null;

            foreach (Authentication user in userDatabase.Users) {
                if (LastAuth.Username.Equals(user.Username) && LastAuth.Password.Equals(user.Password)) {
                    userToUpdate = user;
                    break;
                }
            }

            if (userToUpdate == null) {
                throw new Exception("User not found!");
            }

            try {
                userDatabase.Users.Insert(userDatabase.Users.IndexOf(userToUpdate), UpdateAuth);
                userDatabase.Users.Remove(userToUpdate);
                StreamWriter myWriter = new StreamWriter(XML_FILE);
                mySerializer.Serialize(myWriter, userDatabase);
                myWriter.Close();
                return true;
            }
            catch {
                return false;
            }

        }

    }
}
